import { HttpException, HttpStatus, Injectable } from '@nestjs/common'
import { CreateUserDto } from './dto/create-user.dto'
import { UpdateUserDto } from './dto/update-user.dto'
import { ChangePasswordDto } from 'src/auth/dto/auth.dto'
import { Repository, FindOptionsSelect, QueryFailedError, Like } from 'typeorm'
import { User } from './entities/user.entity'
import * as md5 from 'md5'
import { COMMON_PASSWORD, MD5_SECRET_KEY } from 'config'
import { InjectRepository } from '@nestjs/typeorm'
import { MenuService } from 'src/auth/menu/menu.service'
import { BindRoleDto } from 'src/auth/role/dto/create-role.dto'
import { HttpExceptionEnum } from 'types/http-exception'
import { KeywordPageQuery } from 'types/global'
@Injectable()
export class UserService {
  constructor(
    @InjectRepository(User) private readonly userRepository: Repository<User>,
    private readonly menuService: MenuService
  ) {}

  async create(createUserDto: CreateUserDto, userId: number) {
    const user = new User()
    let { name, password, nickname, phone } = createUserDto
    user.name = name

    if (!password) password = COMMON_PASSWORD
    user.password = md5(password, MD5_SECRET_KEY)
    user.nickname = nickname
    user.phone = phone
    user.createdBy = userId
    user.updatedBy = userId
    try {
      await this.userRepository.save(user)
    } catch (error) {
      throw new HttpException(
        HttpExceptionEnum.USER_EXISTED,
        HttpStatus.BAD_REQUEST
      )
    }
    return {
      id: user.id,
      name: user.name,
      nickname: user.nickname
    }
  }

  async findAll(params: KeywordPageQuery) {
    const { keyword = '', pageNum = 1, pageSize = 10 } = params
    const skip = (pageNum - 1) * pageSize
    const queryBuilder = this.userRepository.createQueryBuilder('user')

    if (keyword) {
      queryBuilder.where(
        'user.name LIKE :keyword OR user.nickname LIKE :keyword',
        { keyword: `%${keyword}%` }
      )
    }

    // 按 createTime 字段降序排序
    queryBuilder.orderBy('user.createTime', 'DESC')
    // 使用 leftJoinAndSelect 关联 createdBy 和 updatedBy
    queryBuilder
      .leftJoinAndSelect('user.createdBy', 'createdBy')
      .leftJoinAndSelect('user.updatedBy', 'updatedBy')

    const [list, total] = await Promise.all([
      queryBuilder
        .skip(skip)
        .take(pageSize)
        .select([
          'user.id as id',
          'user.name as name',
          'user.nickname as nickname',
          'user.phone as phone',
          'user.roleId as roleId',
          'user.avatar as avatar',
          'user.description as description',
          'user.createTime as createTime',
          'user.updateTime as updateTime',
          'createdBy.id as createdById',
          'createdBy.name as createdByName',
          'updatedBy.id as updatedById',
          'updatedBy.name as updatedByName'
        ])
        .getRawMany(),
      queryBuilder.getCount()
    ])
    return {
      list,
      total,
      pageNum,
      pageSize
    }
  }

  async findOne(id: number, showPassword: boolean = false) {
    let selects = [
      'user.id as id',
      'user.name as name',
      'user.nickname as nickname',
      'user.phone as phone',
      'user.role_id as roleId',
      'user.avatar as avatar',
      'user.description as description',
      'user.email as email',
      'role.name as roleName', // 确保选择角色的名称
      'role.code as roleCode' // 确保选择角色的编码
    ]
    if (showPassword) selects.push('user.password as password')
    const queryBuilder = this.userRepository
      .createQueryBuilder('user')
      .leftJoinAndSelect('user.role', 'role')
      .select(selects)
      .where('user.id = :id', { id })

    const result = await queryBuilder.getRawOne()
    if (!result)
      throw new HttpException(
        HttpExceptionEnum.USER_NOT_EXIST,
        HttpStatus.BAD_REQUEST
      )
    return result
  }
  /**
   * 验证用户名密码
   */
  async validateUser(name: string, password: string): Promise<any> {
    const user = await this.findByUsername(name, true)

    if (md5(password, MD5_SECRET_KEY) === user.password) {
      return user
    } else {
      throw new HttpException(
        HttpExceptionEnum.USER_PASSWORD_ERROR,
        HttpStatus.BAD_REQUEST
      )
    }
  }

  async findByUsername(name: string, showPassword: boolean = false) {
    let selects = [
      'user.id as id',
      'user.name as name',
      'user.nickname as nickname',
      'user.phone as phone',
      'user.role_id as roleId',
      'user.avatar as avatar',
      'user.description as description',
      'user.email as email',
      'role.name as roleName', // 确保选择角色的名称
      'role.code as roleCode' // 确保选择角色的编码
    ]
    if (showPassword) selects.push('user.password as password')
    const queryBuilder = this.userRepository
      .createQueryBuilder('user')
      .leftJoinAndSelect('user.role', 'role')
      .select(selects)
      .where('user.name = :name', { name })

    const result = await queryBuilder.getRawOne()

    if (!result)
      throw new HttpException(
        HttpExceptionEnum.USER_NOT_EXIST,
        HttpStatus.BAD_REQUEST
      )

    return result
  }

  async update(updateUserDto: UpdateUserDto, userId: number) {
    try {
      // 1. 从数据库中查找用户
      const user = await this.findOne(updateUserDto.id)

      if (!user) {
        throw new HttpException(
          HttpExceptionEnum.USER_NOT_EXIST,
          HttpStatus.BAD_REQUEST
        )
      }
      if (updateUserDto?.password)
        updateUserDto.password = md5(updateUserDto.password, MD5_SECRET_KEY)
      // 2. 更新用户信息
      Object.assign(user, updateUserDto, { updatedBy: userId })

      // 3. 保存更新后的用户信息
      const newUser = await this.userRepository.save(user)
      delete newUser.password
      // 4. 返回更新结果
      return newUser
    } catch (error) {
      console.log('update user error by function update() in user.service.ts')
      console.log(error)

      if (error instanceof QueryFailedError) {
        throw new HttpException(
          HttpExceptionEnum.USER_EXISTED,
          HttpStatus.BAD_REQUEST
        )
      }
      throw error
    }
  }

  async remove(id: number) {
    try {
      // 1. 从数据库中查找用户
      const user = await this.findOne(id)
      if (!user) {
        throw new HttpException(
          HttpExceptionEnum.USER_NOT_EXIST,
          HttpStatus.BAD_REQUEST
        )
      }

      // 2. 删除用户
      await this.userRepository.remove(user)

      // 3. 返回删除成功的信息
      return `User with id ${id} has been removed`
    } catch (error) {
      console.log(error)

      throw error
    }
  }

  /**
   * 用户绑定角色
   * @param bindRole
   * @returns
   */
  async bindRole(bindRole: BindRoleDto) {
    const user = await this.findOne(bindRole.userId)
    user.roleId = bindRole.roleId
    await this.userRepository.save(user)
    return {
      id: user.id,
      name: user.name,
      nickname: user.nickname,
      roleId: user.roleId
    }
  }

  /**
   * @description 重置密码
   * @param id 用户id
   * @returns password 重置后的密码
   */
  async resetPassword(id: number) {
    const user = await this.findOne(id)
    user.password = md5(COMMON_PASSWORD, MD5_SECRET_KEY)
    await this.userRepository.save(user)
    return COMMON_PASSWORD
  }

  /**
   * 变更密码
   * @param id
   * @param changePasswordDto
   */
  async changePassword(id: number, changePasswordDto: ChangePasswordDto) {
    // 1.  从数据库中查找用户
    const user = await this.findOne(id, true)
    if (!user) {
      throw new HttpException(
        HttpExceptionEnum.USER_NOT_EXIST,
        HttpStatus.BAD_REQUEST
      )
    }
    // 2.  验证旧密码是否正确
    if (md5(changePasswordDto.oldPassword, MD5_SECRET_KEY) !== user.password) {
      throw new HttpException(
        HttpExceptionEnum.USER_PASSWORD_NOT_MATCH,
        HttpStatus.BAD_REQUEST
      )
    }
    // 3.  更新用户的密码
    user.password = md5(changePasswordDto.newPassword, MD5_SECRET_KEY)
    // 4.  返回更新结果
    const {
      email,
      id: userId,
      name,
      nickname
    } = await this.userRepository.save(user)
    return {
      email,
      id: userId,
      name,
      nickname
    }
  }
}
